//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_UTILITY_UANY_H_
#define _BUOLA_UTILITY_UANY_H_

#include <buola/buola.h>
#include <typeinfo>

namespace buola {

/// \addtogroup utility
///@{

////////////////////////////////////////////////////////////////////////////

/// variant type

/// It can contain a value of any type
///
////////////////////////////////////////////////////////////////////////////

class UAny
{
    ///base class to hold the content of a UAny
    class SContentBase
    {
    public:
        virtual ~SContentBase() {}

        virtual const std::type_info &Type() const=0;
        virtual SContentBase *clone() const=0;
    };

    ///holds a tValue object as the content of a UAny
    template<typename tValue>
    class SContent : public SContentBase
    {
    public:
        SContent(const tValue &pValue)
            :   mValue(pValue)
        {}
    public:
        virtual const std::type_info &Type() const       {   return typeid(tValue);          }
        virtual SContentBase *clone() const                 {   return new SContent(mValue);    }
        const tValue &Value()   {   return mValue;  }

    private:
        tValue mValue;
    };

public:
//! \name constructor and destructor
//@{
    ///create an empty value
    UAny()
        :   mContent(nullptr)
    {}

    ///copy constructor
    UAny(const UAny &pO)
        :   mContent(pO.mContent?pO.mContent->clone():nullptr)
    {}

    ///move constructor
    UAny(UAny &&pO)
        :   mContent(pO.mContent)
    {
        pO.mContent=nullptr;
    }

    ///construct from a value of any type
    template<typename tType>
    explicit UAny(const tType &pValue)
        :   mContent(new SContent<tType>(pValue))
    {}

    ///destructor
    ~UAny()
    {
        delete mContent;
    }
//@}

    ///returns true if it contains no value
    bool empty() const
    {
        return (!mContent);
    }

    ///swaps the content of two values
    void swap(UAny &pRH)
    {
        std::swap(mContent,pRH.mContent);
    }

    ///assign a value of any type
    template<typename tType>
    UAny &operator=(const tType &pRH)
    {
        UAny(pRH).swap(*this);
        return *this;
    }

    ///copy assignment operator
    UAny &operator=(const UAny &pRH)
    {
        UAny(pRH).swap(*this);
        return *this;
    }

    ///move assignment operator
    UAny &operator=(UAny&& pRH)
    {
        swap(pRH);
        return *this;
    }
    
    ///assign an empty value
    void clear()
    {
        delete mContent;
        mContent=nullptr;
    }

    ///returns true if the content is of type \p tType
    template<typename tType>
    bool HasType() const
    {
        return (mContent?mContent->Type():typeid(void))==typeid(tType);
    }

    ///get the content, which must be of type tType

    ///\throws XInvalid if there is no value or it is of another type
    ///\returns a reference to the content
    template<typename tType>
    const tType &Get() const
    {
        if(!HasType<tType>())
            throw XInvalid("bad cast in UAny::Get");
        return static_cast<SContent<tType>*>(mContent)->Value();
    }
    
    template<typename tType>
    void Set(const tType &pRH)
    {
        UAny(pRH).swap(*this);
    }

private:
    SContentBase *mContent;
};

///@}

/*namespace buola*/ }

#endif
